package org.ym.tools;

import eu.bitwalker.useragentutils.Browser;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Enumeration;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author ym
 * @date 2022-06-06 15:30:47
 */
public class RequestTool {

    public static final String USER_AGENT = "User-Agent";
    public static final String CONTENT_DISPOSITION = "Content-Disposition";
    public static final String MSIE = "MSIE";
    public static final String TRIDENT = "Trident";
    public static final String JAVAX_SERVLET_ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
    public static final String JAVAX_SERVLET_FORWARD_REQUEST_URI = "javax.servlet.forward.request_uri";
    public static final String JAVAX_SERVLET_INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
    public static final String X_FORWARDED_FOR = "x-forwarded-for";
    public static final String PROXY_CLIENT_IP = "Proxy-Client-IP";
    public static final String WL_PROXY_CLIENT_IP = "WL-Proxy-Client-IP";
    public static final String HTTP_CLIENT_IP = "HTTP_CLIENT_IP";
    public static final String HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR";
    public static final String CLIENT_IP = "clientip";
    public static final String UNKNOWN = "unknown";

    private RequestTool() {
    }

    /**
     * 获取 http://localhost:8080/ctx 信息
     *
     * @param req 请求
     * @return headerUrl
     */
    public static String getRequestHeaderUrl(HttpServletRequest req) {
        if (StringUtils.isNotBlank(req.getContextPath())) {
            return req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort() + "/" + req.getContextPath();
        } else {
            return req.getScheme() + "://" + req.getServerName() + ":" + req.getServerPort();
        }
    }

    /**
     * UTF-8编码读取HttpServletRequest的body体
     *
     * @param request 请求
     * @return String
     */
    public static String readBody(HttpServletRequest request) {
        return readBody(request, StandardCharsets.UTF_8);
    }

    /**
     * UTF-8编码读取HttpServletRequest的body体
     *
     * @param request 请求
     * @return T
     */
    public static <T> T readBodyToJson(HttpServletRequest request,Class<T> classZ) {
        String body = readBody(request, StandardCharsets.UTF_8);
        if(StringUtils.isNotBlank(body)){
            return GsonTool.getGson().fromJson(body,classZ);
        }
        return null;
    }

    /**
     * 读取HttpServletRequest的body体
     *
     * @param request 请求
     * @param charset 字符编码
     * @return 数据
     */
    public static String readBody(HttpServletRequest request, Charset charset) {
        String body = "";
        if (request != null) {
            StringBuilder stringBuilder = new StringBuilder();
            String str;
            try (BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), charset))) {
                while ((str = reader.readLine()) != null) {
                    stringBuilder.append(str);
                }
                body = stringBuilder.toString();
            } catch (Exception e) {
                e.printStackTrace();
                // doNothing
            }
        }
        return body;
    }

    /**
     * 根据HttpServletRequest获取浏览器信息
     *
     * @param request HttpServletRequest
     * @return 浏览器信息
     */

    public static Browser getBrowser(HttpServletRequest request) {
        String agent = request.getHeader(USER_AGENT);
        //解析agent字符串
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
        return userAgent.getBrowser();
    }

    /**
     * 根据HttpServletRequest获取操作系统信息
     *
     * @param request HttpServletRequest
     * @return 操作系统信息
     */
    public static OperatingSystem getOperatingSystem(HttpServletRequest request) {
        String agent = request.getHeader(USER_AGENT);
        //解析agent字符串
        UserAgent userAgent = UserAgent.parseUserAgentString(agent);
        return userAgent.getOperatingSystem();
    }

    /**
     * firefox，谷歌 Trident是标识是ie浏览器 特别处理ie11 的问题
     *
     * @param response  返回
     * @param userAgent useragent
     * @param fileName  文件名
     * @throws IOException IO错误
     */
    public static void setResponseFileName(HttpServletResponse response, String userAgent, String fileName) throws IOException {
        if (userAgent != null && !userAgent.contains(MSIE) && !userAgent.contains(TRIDENT)) {
            String enableFileName = "=?UTF-8?B?" + Base64Tool.encode(fileName) + "?=";
            response.setHeader(CONTENT_DISPOSITION, StringTool.concatStr("attachment; filename=", enableFileName));
        } else {
            //ie
            fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
            response.addHeader(CONTENT_DISPOSITION, StringTool.concatStr("attachment; filename=", fileName));
        }
    }

    /**
     * 取字符串类型的参数。 如果取得的值为null，则返回默认字符串。
     *
     * @param request      请求
     * @param key          字段名
     * @param defaultValue 默认值
     * @return 数据
     */
    public static String getString(HttpServletRequest request, String key,
                                   String defaultValue, boolean b) {
        String value = request.getParameter(key);
        if (StringUtils.isBlank(value)) {
            return defaultValue;
        }
        value = value.trim();
        if (b) {
            return value.replace("'", "''");
        } else {
            return value;
        }
    }

    /**
     * 取字符串类型的参数。 如果取得的值为null，则返回空字符串。
     *
     * @param request 请求
     * @param key     字段名
     * @return 数据
     */
    public static String getString(HttpServletRequest request, String key) {
        return getString(request, key, "", true);
    }

    /**
     * 取得安全字符串。
     *
     * @param request      请求
     * @param key          字段名
     * @param defaultValue 默认值
     * @return 数据
     */
    public static String getSecureString(HttpServletRequest request,
                                         String key, String defaultValue) {
        String value = request.getParameter(key);
        if (StringUtils.isBlank(value)) {
            return defaultValue;
        }
        value = value.trim();
        return filterInject(value);

    }

    /**
     * 取得安全字符串，防止程序sql注入，脚本攻击。
     *
     * @param request 请求
     * @param key 字段名
     * @return 数据
     */
    public static String getSecureString(HttpServletRequest request, String key) {
        return getSecureString(request, key, "");
    }

    private static final String INJECT_CHECK_STRING = "\\||;|insert|select|delete|update|chr|truncate|char";
    private static final Pattern INJECT_CHECK_PATTERN = Pattern.compile(INJECT_CHECK_STRING, Pattern.CANON_EQ
            | Pattern.DOTALL | Pattern.CASE_INSENSITIVE
            | Pattern.UNICODE_CASE);

    /**
     * 过滤script|iframe|\\||;|exec|insert|select|delete|update|count|chr|truncate
     * |char字符串 防止SQL注入
     *
     * @param str 请求数据
     * @return 数据
     */
    public static String filterInject(String str) {
        Matcher matcher = INJECT_CHECK_PATTERN.matcher(str);
        str = matcher.replaceAll("");
        str = str.replace("'", "''");
        str = str.replace("-", "—");
        str = str.replace("(", "（");
        str = str.replace(")", "）");
        str = str.replace("%", "％");
        return str;
    }

    /**
     * 从request中取得int值
     *
     * @param request 请求
     * @param key     字段名
     * @return 数据
     */
    public static int getInt(HttpServletRequest request, String key) {
        return getInt(request, key, 0);
    }

    /**
     * 从request中取得int值,如果无值则返回缺省值
     *
     * @param request 请求
     * @param key     字段名
     * @param defaultValue 默认值
     * @return 数据
     */
    public static int getInt(HttpServletRequest request, String key, int defaultValue) {
        String str = request.getParameter(key);
        if (StringUtils.isBlank(str)) {
            return defaultValue;
        }
        str = str.trim();
        return NumberTool.parseInt(str,defaultValue);
    }

    /**
     * 从Request中取得long值
     *
     * @param request 请求
     * @param key 字段名
     * @return 数据
     */
    public static long getLong(HttpServletRequest request, String key) {
        return getLong(request, key, 0);
    }

    /**
     * 从Request中取得long值,如果无值则返回缺省值
     *
     * @param request 请求
     * @param key 字段名
     * @param defaultValue 默认值
     * @return 数据
     */
    public static long getLong(HttpServletRequest request, String key, long defaultValue) {
        String str = request.getParameter(key);
        if (StringUtils.isBlank(str)) {
            return defaultValue;
        }
        str = str.trim();
        return NumberTool.parseLong(str,defaultValue);
    }

    /**
     * 从Request中取得float值
     *
     * @param request 请求
     * @param key 字段名
     * @return 数据
     */
    public static float getFloat(HttpServletRequest request, String key) {
        return getFloat(request, key, 0);
    }

    /**
     * 从Request中取得float值,如无值则返回缺省值
     *
     * @param request 请求
     * @param key 字段名
     * @param defaultValue 默认值
     * @return 数据
     */
    public static float getFloat(HttpServletRequest request, String key, float defaultValue) {
        String str = request.getParameter(key);
        if (StringUtils.isBlank(str)) {
            return defaultValue;
        }
        str = str.trim();
        return NumberTool.parseFloat(str,defaultValue);
    }

    /**
     * 从Request中取得boolean值,如无值则返回缺省值 false, 如值为数字1，则返回true
     *
     * @param request 请求
     * @param key 字段名
     * @return 数据
     */
    public static boolean getBoolean(HttpServletRequest request, String key) {
        return getBoolean(request, key, false);
    }

    /**
     * 从Request中取得boolean值 对字符串,如无值则返回缺省值, 如值为数字1，则返回true
     *
     * @param request 请求
     * @param key 字段名
     * @param defaultValue 默认值
     * @return 数据
     */
    public static boolean getBoolean(HttpServletRequest request, String key, boolean defaultValue) {
        String str = request.getParameter(key);
        if (StringUtils.isBlank(str)) {
            return defaultValue;
        }
        str = str.trim();
        if (StringUtils.isNumeric(str)) {
            return Integer.parseInt(str) != 0;
        }
        return NumberTool.parseBoolean(str,defaultValue);
    }

    /**
     * 从Request中取得boolean值,如无值则返回缺省值 0
     *
     * @param request 请求
     * @param key 字段名
     * @return 数据
     */
    public static Short getShort(HttpServletRequest request, String key) {
        return getShort(request, key, (short) 0);
    }

    /**
     * 从Request中取得Short值 对字符串,如无值则返回缺省值
     *
     * @param request 请求
     * @param key 字段名
     * @param defaultValue 默认值
     * @return 数据
     */
    public static Short getShort(HttpServletRequest request, String key, Short defaultValue) {
        String str = request.getParameter(key);
        if (StringUtils.isBlank(str)) {
            return defaultValue;
        }
        str = str.trim();
        return NumberTool.parseShort(str);
    }

    /**
     * 从Request中取得Date值,如无值则返回缺省值,如有值则返回 yyyy-MM-dd HH:mm:ss 格式的日期,或者自定义格式的日期
     *
     * @param request 请求
     * @param key 字段名
     * @param style 日期格式，默认为yyyy-MM-dd HH:mm:ss
     * @return 数据
     */
    public static Date getDate(HttpServletRequest request, String key, String style) {
        String str = request.getParameter(key);
        if (StringUtils.isBlank(str)) {
            return null;
        }
        str = str.trim();
        return DateTimeTool.parseDate(str, style);
    }

    /**
     * 从Request中取得Date值,如无值则返回缺省值null, 如果有值则返回 yyyy-MM-dd格式的日期
     *
     * @param request 请求
     * @param key 字段名
     * @return 数据
     */
    public static Date getDate(HttpServletRequest request, String key) {
        return getDate(request, key, DateTimeTool.FORMAT_DATE);
    }

    /**
     * 从Request中取得Date值,如无值则返回缺省值 如有值则返回 yyyy-MM-dd HH:mm:ss 格式的日期
     *
     * @param request 请求
     * @param key 字段名
     * @return 数据
     */
    public static Date getTimestamp(HttpServletRequest request, String key) {
        return getDate(request, key, DateTimeTool.FORMAT_DATE_TIME);
    }

    /**
     * 取得当前页URL,如有参数则带参数
     *
     * @param request request
     * @return 当前页url
     */
    public static String getUrl(HttpServletRequest request) {
        StringBuilder urlThisPage = new StringBuilder();
        urlThisPage.append(request.getRequestURI());
        Enumeration<?> e = request.getParameterNames();
        String para;
        String values;
        urlThisPage.append("?");
        while (e.hasMoreElements()) {
            para = (String) e.nextElement();
            values = request.getParameter(para);
            urlThisPage.append(para);
            urlThisPage.append("=");
            urlThisPage.append(values);
            urlThisPage.append("&");
        }
        return urlThisPage.substring(0, urlThisPage.length() - 1);
    }

    /**
     * 取得local。
     *
     * @param request 请求
     * @return local 本土语言
     */
    public static Locale getLocal(HttpServletRequest request) {
        Locale local = request.getLocale();
        if (local == null) {
            local = Locale.CHINA;
        }
        return local;
    }

    /**
     * 获取出错的url
     *
     * @param request 请求
     * @return 出错url
     */
    public static String getErrorUrl(HttpServletRequest request) {
        String errorUrl = (String) request
                .getAttribute(JAVAX_SERVLET_ERROR_REQUEST_URI);
        if (errorUrl == null) {
            errorUrl = (String) request
                    .getAttribute(JAVAX_SERVLET_FORWARD_REQUEST_URI);
        }
        if (errorUrl == null) {
            errorUrl = (String) request
                    .getAttribute(JAVAX_SERVLET_INCLUDE_REQUEST_URI);
        }
        if (errorUrl == null) {
            errorUrl = request.getRequestURL().toString();
        }
        return errorUrl;
    }

    /**
     * 是否ip地址是否为空
     * @param ip ip地址
     * @return true：空，false：非空
     */
    private static boolean checkIPIsNull(String ip){
        return ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip);
    }

    /**
     * 获取IP地址
     *
     * @param request 请求
     * @return ip地址
     */
    public static String getIPAddress(HttpServletRequest request) {
        String ip = request.getHeader(X_FORWARDED_FOR);
        if (checkIPIsNull(ip)) {
            ip = request.getHeader(PROXY_CLIENT_IP);
        }
        if (checkIPIsNull(ip)) {
            ip = request.getHeader(WL_PROXY_CLIENT_IP);
        }
        if (checkIPIsNull(ip)) {
            ip = request.getHeader(HTTP_CLIENT_IP);
        }
        if (checkIPIsNull(ip)) {
            ip = request.getHeader(HTTP_X_FORWARDED_FOR);
        }
        if (checkIPIsNull(ip)) {
            ip = request.getHeader(CLIENT_IP);
        }
        if (checkIPIsNull(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
